// Inspired by react-hot-toast library
import { useState, useEffect, createContext, useContext } from "react";

// -------------------------- 常量配置 --------------------------
// 限制同时显示的Toast最大数量（避免界面被过多提示占用）
const TOAST_LIMIT = 5;
// Toast自动关闭延迟时间（单位：毫秒），此处设为3秒（原30秒优化为更合理的短延迟）
const TOAST_REMOVE_DELAY = 3000;

// -------------------------- 状态操作类型 --------------------------
// 定义Toast状态管理的4种核心操作，避免硬编码字符串导致的错误
const actionTypes = {
  ADD_TOAST: "ADD_TOAST", // 添加新Toast
  UPDATE_TOAST: "UPDATE_TOAST", // 更新已有Toast（如内容、状态）
  DISMISS_TOAST: "DISMISS_TOAST", // 标记Toast为"待关闭"（通常触发后续移除）
  REMOVE_TOAST: "REMOVE_TOAST", // 从状态中彻底移除Toast
};

// -------------------------- 工具函数 --------------------------
// 生成Toast唯一ID（避免重复，用于定位和操作特定Toast）
let count = 0;
function genId() {
  count = (count + 1) % Number.MAX_VALUE; // 取模防止数字过大溢出
  return count.toString();
}

// -------------------------- 计时器管理 --------------------------
// 存储Toast的自动关闭计时器（key: toastId, value: 计时器ID）
const toastTimeouts = new Map();

/**
 * 添加Toast到自动关闭队列
 * @param {string} toastId - Toast唯一标识
 * @param {number} [delay=TOAST_REMOVE_DELAY] - 自定义延迟（默认使用全局配置）
 */
const addToRemoveQueue = (toastId, delay = TOAST_REMOVE_DELAY) => {
  // 避免重复添加同一Toast的计时器
  if (toastTimeouts.has(toastId)) {
    return;
  }

  // 延迟后执行"彻底移除"操作
  const timeout = setTimeout(() => {
    toastTimeouts.delete(toastId); // 清除计时器记录
    dispatch({
      type: actionTypes.REMOVE_TOAST,
      toastId,
    });
  }, delay);

  toastTimeouts.set(toastId, timeout);
};

/**
 * 从自动关闭队列中清除指定Toast的计时器
 * @param {string} toastId - Toast唯一标识
 * 作用：手动关闭Toast时，取消其自动关闭计时器，避免重复操作
 */
const clearFromRemoveQueue = (toastId) => {
  const timeout = toastTimeouts.get(toastId);
  if (timeout) {
    clearTimeout(timeout); // 取消计时器
    toastTimeouts.delete(toastId); // 移除记录
  }
};

// -------------------------- 状态 reducer --------------------------
/**
 * 处理Toast状态变更的纯函数（给定当前状态和操作，返回新状态）
 * @param {Object} state - 当前状态（{ toasts: [] }）
 * @param {Object} action - 操作指令（{ type: 操作类型, toast/toastId: 数据 }）
 * @returns {Object} 新状态
 */
export const reducer = (state, action) => {
  switch (action.type) {
    // 添加新Toast：将新Toast插入数组头部，同时限制最大数量
    case actionTypes.ADD_TOAST:
      return {
        ...state,
        toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
      };

    // 更新Toast：找到对应ID的Toast，合并新属性（如open状态、内容）
    case actionTypes.UPDATE_TOAST:
      return {
        ...state,
        toasts: state.toasts.map((t) =>
          t.id === action.toast.id ? { ...t, ...action.toast } : t
        ),
      };

    // 标记Toast为"待关闭"：将目标Toast的open设为false（用于UI隐藏过渡），并加入自动关闭队列
    case actionTypes.DISMISS_TOAST: {
      const { toastId } = action;

      // 单个Toast：添加到自动关闭队列
      if (toastId) {
        addToRemoveQueue(toastId);
      } else {
        // 全部Toast：遍历所有Toast添加到队列
        state.toasts.forEach((toast) => {
          addToRemoveQueue(toast.id);
        });
      }

      // 更新open状态为false（UI层可根据此状态做隐藏动画）
      return {
        ...state,
        toasts: state.toasts.map((t) =>
          t.id === toastId || toastId === undefined
            ? { ...t, open: false }
            : t
        ),
      };
    }

    // 彻底移除Toast：从状态数组中过滤掉目标Toast
    case actionTypes.REMOVE_TOAST:
      if (action.toastId === undefined) {
        // 无ID：清空所有Toast
        return { ...state, toasts: [] };
      }
      // 有ID：移除指定Toast
      return {
        ...state,
        toasts: state.toasts.filter((t) => t.id !== action.toastId),
      };

    // 默认：返回原状态（防止未定义的操作类型破坏状态）
    default:
      return state;
  }
};

// -------------------------- 全局状态管理 --------------------------
// 存储状态变更的监听函数（用于跨组件同步状态）
const listeners = [];
// 内存中的全局状态（初始为空数组）
let memoryState = { toasts: [] };

/**
 * 触发状态变更的调度函数
 * @param {Object} action - 操作指令
 * 作用：执行reducer更新状态，并通知所有监听者（触发组件重渲染）
 */
function dispatch(action) {
  memoryState = reducer(memoryState, action); // 计算新状态
  // 通知所有监听组件更新
  listeners.forEach((listener) => {
    listener({ ...memoryState }); // 传递新状态的副本，避免直接修改原对象
  });
}

// -------------------------- Toast 核心方法 --------------------------
/**
 * 创建新Toast的工厂函数
 * @param {Object} props - Toast配置（如title、description、variant等）
 * @returns {Object} 操作当前Toast的方法（id: 唯一标识, dismiss: 关闭, update: 更新）
 */
function toast({ ...props }) {
  const id = genId(); // 生成当前Toast的唯一ID

  /**
   * 更新当前Toast的内容或状态
   * @param {Object} props - 要更新的属性（如description、open等）
   */
  const update = (props) =>
    dispatch({
      type: actionTypes.UPDATE_TOAST,
      toast: { ...props, id },
    });

  /**
   * 手动关闭当前Toast的核心逻辑
   * 优化点：1. 取消自动关闭计时器 2. 标记为关闭状态 3. 立即彻底移除（不等待延迟）
   */
  const dismiss = () => {
    clearFromRemoveQueue(id); // 1. 取消自动关闭（避免手动关闭后仍触发自动移除）
    // 2. 更新open为false（供UI层做隐藏过渡）
    dispatch({
      type: actionTypes.UPDATE_TOAST,
      toast: { id, open: false },
    });
    // 3. 立即从状态中移除（解决"关闭后仍残留"问题）
    dispatch({ type: actionTypes.REMOVE_TOAST, toastId: id });
  };

  // 1. 触发"添加Toast"操作，将新Toast加入全局状态
  dispatch({
    type: actionTypes.ADD_TOAST,
    toast: {
      ...props, // 透传用户配置（如标题、描述、样式变体）
      id, // 绑定唯一ID
      open: true, // 初始状态：显示
      // 监听open状态变化（若外部修改open为false，触发关闭）
      // onOpenChange: (open) => {
      //   if (!open) dismiss();
      // },
      onClick: dismiss, // 点击Toast时直接触发关闭（简化手动关闭交互）
    },
  });

  // 2. 为当前Toast添加自动关闭计时器（实现"3秒后自动消失"）
  addToRemoveQueue(id);

  // 返回操作当前Toast的方法（供外部手动控制）
  return { id, dismiss, update };
}

// -------------------------- 自定义 Hook（供组件使用） --------------------------
/**
 * 供React组件使用的Toast Hook
 * 作用：让组件获取全局Toast状态、创建Toast、关闭Toast的能力
 * @returns {Object} { toasts: 所有Toast列表, toast: 创建Toast的方法, dismiss: 关闭Toast的方法 }
 */
function useToast() {
  // 组件内部状态，同步全局memoryState
  const [state, setState] = useState(memoryState);

  // 监听全局状态变更：组件挂载时添加监听，卸载时移除（避免内存泄漏）
  useEffect(() => {
    // 状态变更时，更新组件内部state（触发重渲染）
    const listener = (newState) => {
      setState(newState);
    };
    listeners.push(listener); // 加入全局监听队列

    // 清理函数：组件卸载时移除监听
    return () => {
      const index = listeners.indexOf(listener);
      if (index > -1) {
        listeners.splice(index, 1);
      }
    };
  }, []); // 空依赖：仅在组件挂载/卸载时执行一次

  // 返回给组件的API
  return {
    ...state, // 包含toasts列表（供Toast容器渲染所有提示）
    toast, // 创建新Toast的方法（如toast({ title: "成功" })）
    /**
     * 手动关闭Toast的方法（支持单个/全部）
     * @param {string} [toastId] - 可选，指定要关闭的Toast ID；无ID则关闭所有
     */
    dismiss: (toastId) => {
      if (toastId) {
        // 关闭单个Toast：取消计时器 + 立即移除
        clearFromRemoveQueue(toastId);
        dispatch({ type: actionTypes.REMOVE_TOAST, toastId });
      } else {
        // 关闭所有Toast：取消所有计时器 + 清空状态
        state.toasts.forEach((toast) => clearFromRemoveQueue(toast.id));
        dispatch({ type: actionTypes.REMOVE_TOAST });
      }
    },
  };
}

// 导出供外部使用（Hook和创建Toast的方法）
export { useToast, toast };